/*
 * Bspline.cpp
 *
 *  Created on: 24/11/2009
 *      Author: leonardo
 */

#include "Bspline.h"
#include <GL/glut.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define PUNTOS_RESOLUCION 1000
#define PUNTOS_CONTROL 	4
#define PADDING  100.0

Bspline::Bspline()
{

}

void Bspline::dibujarCurva()
{
	int *u;
	int num_output = PUNTOS_RESOLUCION;
	double increment, interval;
	Punto calcxyz;
	int output_index;
	int n = getPtsControl().size() - 1;
	int t = 4; // grado de la curva-- cubica

	u = new int[n + t + 1];
	compute_intervals(u, n, t);

	increment = (double) (n - t + 2) / (num_output - 1);
	interval = 0;

	for (output_index = 0; output_index < num_output - 1; output_index++)
	{
		compute_point(u, n, t, interval,calcxyz);
		agregarPuntoCurva(calcxyz);
		interval = interval + increment; // increment los parametros
	}
	agregarPuntoCurva(getPtsControl()[n]); //pone el ultimo punto
	delete u;
}

double Bspline::blend(int k, int t, int *u, double v)
{
	double value;

	if (t == 1)
	{
		if ((u[k] <= v) && (v < u[k + 1]))
			value = 1;
		else
			value = 0;
	}
	else
	{
		if ((u[k + t - 1] == u[k]) && (u[k + t] == u[k + 1])) // check for divide by zero
			value = 0;
		else if (u[k + t - 1] == u[k]) // if a term's denominator is zero,use just the other
			value = (u[k + t] - v) / (u[k + t] - u[k + 1]) * blend(k + 1,t - 1, u, v);
		else if (u[k + t] == u[k + 1])
			value = (v - u[k]) / (u[k + t - 1] - u[k]) * blend(k, t - 1, u, v);
		else
			value = (v - u[k]) / (u[k + t - 1] - u[k]) * blend(k, t - 1, u, v) + (u[k + t] - v) / (u[k + t] - u[k + 1]) * blend(k + 1, t- 1, u, v);
	}

	return value;
}


void Bspline::compute_intervals(int *u, int n, int t)   // figure out the knots
{
	int j;
	for (j = 0; j <= n + t; j++)
	{
		if (j < t)
			u[j] = 0;
		else if ((t <= j) && (j <= n))
			u[j] = j - t + 1;
		else if (j > n)
			u[j] = n - t + 2; // if n-t=-2 then we're screwed, everything goes to 0
	}
}

void Bspline::compute_point(int *u, int n, int t, double v,Punto &output)
{
	int k;
	double temp;

	// initialize the variables that will hold our outputted point
	output.x(0);
	output.y(0);
	output.z(0);
	for (k = 0; k <= n; k++)
	{
		temp = blend(k, t, u, v); // same blend is used for each dimension coordinate
		output.x(output.x() + (getPtsControl()[k]).x() * temp);
		output.y(output.y() + (getPtsControl()[k]).y() * temp);
		output.z(output.z() + (getPtsControl()[k]).z() * temp);
	}
}


void Bspline::dibujarCurvaRandom(Punto inicio,Punto fin)
{
	int i = 0;
	float limite = 20.0;
	float randX;
	float randY;
	float randZ;

	agregarPuntoControl(inicio);
	agregarPuntoControl(inicio);
	agregarPuntoControl(inicio);

    time_t t1;
    (void) time(&t1);
    srand48((long) t1);

	randX = drand48() * limite;
	randY = drand48() * limite;
	randZ = inicio.z();
	agregarPuntoControl(Punto(randX,randY,randZ));
	for ( i = 4 ; i < PUNTOS_CONTROL + 2 ; i++)
	{
		randX = drand48() * limite;
		randY = drand48() * limite;
		randZ = drand48() * limite;
		agregarPuntoControl(Punto(randX,randY,randZ));
	}
	randX = drand48() * limite;
	randY = drand48() * limite;
	randZ = fin.z();
	agregarPuntoControl(Punto(randX,randY,randZ));
	agregarPuntoControl(fin);
	agregarPuntoControl(fin);
	agregarPuntoControl(fin);
	agregarPuntoControl(fin);
	dibujarCurva();
}



Bspline::~Bspline()
{ }
